﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Net.Mail;

using ERPStore.Extensions;

using ERPStore.Web.Routing;
using ERPStore.Web.Controllers.ActionFilters;
using ERPStore.Payment.Paypal.com.paypalobjects;
using ERPStore.Web.Extensions;

using Microsoft.Practices.Unity;

using CloudSoft.Extensions;

namespace ERPStore.Payment.Paypal.Controllers
{
	/// <summary>
	/// Controller pour Paypal
	/// </summary>
	public class PaypalExpressController : ERPStore.Web.Controllers.CheckoutController
	{
		public PaypalExpressController(
			ERPStore.Services.ICartService cartService
			, ERPStore.Services.ISalesService salesService
			, ERPStore.Services.IAccountService accountService
			, ERPStore.Services.IEmailerService emailerService
			, ERPStore.Web.Services.IEmailTemplateService emailTemplateService
			, ERPStore.Services.CryptoService cryptoService
			, ERPStore.Services.IIncentiveService incentiveService
			, ERPStore.Services.IOffersService offerService
			, Models.Settings settings
			)
			: base(cartService, salesService, accountService, emailerService, emailTemplateService, incentiveService, cryptoService, offerService)
		{
			this.Settings = settings;

			// Bof bof
			var container = ERPStore.GlobalConfiguration.Configuration.DependencyResolver.GetContainer();
			PaypalService = (Services.IPaypalExpressService)container.Resolve<ERPStore.Services.IPaymentService>("paypal");
		}

		protected Services.IPaypalExpressService PaypalService { get; private set; }
		protected Models.Settings Settings { get; private set; }

		[ZeroCacheActionFilter]
		[ERPStore.Web.Controllers.ActionFilters.TrackerActionFilter]
		[HttpPost]
		public ActionResult Confirmation(string condition)
		{
			bool confirmation = (condition == "on");

			var cart = CartService.GetCurrentCart(User);
			if (cart == null
				|| cart.ItemCount == 0)
			{
				return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.HOME);
			}

			var user = User.CurrentUser;

			Logger.Info("checkout confirmation with visitor : {0}", User.VisitorId);

			if (user != null)
			{
				if (cart.DeliveryAddress == null
					|| cart.BillingAddress == null)
				{
					Logger.Warn("checkout confirmation without deliveryaddress or billingaddress with user : {0}", user.Email);
					return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.CHECKOUT);
				}
			}
			else
			{
				// Dans le cas ou la création de commande est anonyme
				var registration = AccountService.GetRegistrationUser(User.VisitorId);
				if (registration == null)
				{
					Logger.Warn("checkout confirmation without deliveryaddress or billingaddress with visitor : {0}", User.VisitorId);
					return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.CHECKOUT);
				}
				user = AccountService.CreateUserFromRegistration(registration);
				cart.BillingAddress = user.DefaultAddress;
				cart.DeliveryAddress = user.LastDeliveredAddress;
			}

			SalesService.CalculateShippingFee(cart, User);
			SalesService.ProcessExport(cart, User);
			cart.PaymentModeName = "paypal";

			ViewData.Model = cart;
			if (!confirmation)
			{
				ModelState.AddModelError("condition", "Vous devez accepter nos conditions de ventes pour pouvoir enregistrer cette commande");
				return View(PaypalService.ConfirmationViewName);
			}

			using (var ts = TransactionHelper.GetNewReadCommitted())
			{
				CartService.Save(cart);
				ts.Complete();
			}
			
			var normalRoute = Url.RouteERPStoreUrl("PaypalExpressPaymentAccepted");
			var returnUrl = string.Format("http://{0}{1}", HttpContext.Request.Url.Host, normalRoute);

			var cancelRoute = Url.RouteERPStoreUrl("PaypalExpressPaymentCanceled");
			var cancelUrl = string.Format("http://{0}{1}", HttpContext.Request.Url.Host, cancelRoute);

			var callbackRoute = Url.RouteERPStoreUrl("PaypalExpressPostSale");
			var callbacklUrl = string.Format("http://{0}{1}", HttpContext.Request.Url.Host, callbackRoute);

			string orderDescription = string.Format("Votre commande d'un montant de {0:F2} € TTC sur {1}", cart.GrandTotalWithTax, GlobalConfiguration.Configuration.Settings.SiteName);
			var token = PaypalService.SetExpressCheckout(cart, user, CurrencyCodeType.EUR, orderDescription, returnUrl, cancelUrl, callbacklUrl);
			if (!token.IsNullOrTrimmedEmpty())
			{
				var redirect = PaypalService.GetPaypalCheckoutUrl(token);
				Logger.Debug(redirect);
				return Redirect(redirect);
			}
			else
			{
				ModelState.AddModelError("_FORM", "Des problèmes techniques nous empêchent de communiquer avec les serveurs Paypal, veuillez ressayer ultérieurement");
			}

			return View(PaypalService.ConfirmationViewName);
		}

		[ZeroCacheActionFilter]
		[ERPStore.Web.Controllers.ActionFilters.TrackerActionFilter]
		public ActionResult BackFromPaypal(string token, string payerId)
		{
			if (token.IsNullOrTrimmedEmpty())
			{
				Logger.Warn("back from paypal without token");
				return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.HOME);
			}

			Logger.Info("back from paypal with token : {0}", token);
			GetExpressCheckoutDetailsResponseType details = null;

			try
			{
				details = PaypalService.GetExpressCheckoutDetails(token);
			}
			catch (Exception ex)
			{
				Logger.Error(ex);
			}

			if (details == null)
			{
				ModelState.AddModelError("_FORM", "Un problème technique empeche l'enregistrement de votre commande, veuillez réessayer ultérieurement");
				ViewData["message"] = "Un problème technique empèche l'enregistrement de votre commande, un responsable des ventes vient d'etre notifié de ce problème, nous vous contacterons pour vous tenir au courant";
				return View(Settings.CanceledViewName);
			}

			string errors = null;
			bool success = PaypalService.IsResponseSuccess(details, out errors);
			if (!success)
			{
				Logger.Notification("Paypal en erreur : {0}", errors);
				ViewData["message"] = errors;
				ModelState.AddModelError("_FORM", "Un problème technique empeche l'enregistrement de votre commande, veuillez réessayer ultérieurement");
				return View(Settings.CanceledViewName);
			}

			var cartCode = details.GetExpressCheckoutDetailsResponseDetails.Custom;

			var processResult = BackFromPaypalWithDetails(cartCode, token, payerId, details.GetExpressCheckoutDetailsResponseDetails);
			switch (processResult)
			{
				case "emptycart" : // Back to home, bad token
					Logger.Warn("back from paypal with bad token : {0}", token);
					return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.HOME);
				case "invalidcart" : // Invalid Model
					Logger.Warn("back from paypal with invalid model cart");
					return View(PaypalService.ConfirmationViewName);
				default:
					break;
			}

			return RedirectToERPStoreRoute("PaypalExpressFinalized", new { key = processResult });
		}

		public string BackFromPaypalWithDetails(string cartCode, string token, string payerId, GetExpressCheckoutDetailsResponseDetailsType responseDetails)
		{
			var cart = CartService.GetCartByCode(cartCode);
			if (cart == null
				|| cart.ItemCount == 0)
			{
				return "emptycart";
			}

			ERPStore.Models.Order order = null;
			bool isNewCustomer = false;
			string password = null;
			string transactionId = null;
			try
			{
				order = (ERPStore.Models.Order)base.ProcessResponse(cartCode, out isNewCustomer, out password, PaypalService.Name);
				if (order != null)
				{
					transactionId = PaypalService.DoExpressCheckoutPaymentSale(token, payerId, order, CurrencyCodeType.EUR, order.Code, order.Code);
					SalesService.AddPaymentToOrder(order, PaypalService.Name, transactionId, responseDetails);
				}
				else
				{
					ModelState.AddModelError("_FORM", "Cette commande est déjà prise en compte");
				}
			}
			catch (Exception ex)
			{
				if (order != null
					&& transactionId == null)
				{
					ex.Data.Add("PaypalFailed:OrderCode", order.Code);
					ex.Data.Add("PaypalFailed:CartCode", cartCode);
					SalesService.CancelOrder(order, string.Format("Annulation paypal sur le panier N°{0}", cartCode));
					// Remise dans le circuit du panier
					CartService.ChangeCurrentCart(cart.Code, User);
					CartService.Save(cart);
				}
				Logger.Error(ex);
				ModelState.AddModelError("_FORM", "Un problème technique empeche l'enregistrement de votre commande, veuillez réessayer ultérieurement");
			}

			if (!ModelState.IsValid)
			{
				ViewData.Model = cart;
				return "invalidcart";
			}

			Response.AddAuthenticatedCookie(order.User.Id, true);

			var subject = new
			{
				OrderCode = order.Code,
				ExpirationDate = DateTime.Now.AddDays(1),
				IsNewCustomer = isNewCustomer,
				Password = password,
				CartCode = cart.Code,
			};
			var key = CryptoService.Encrypt(subject);

			Logger.Info("Retour positif du centre de paiement");
			return key;
		}


		[ZeroCacheActionFilter]
		[ERPStore.Web.Controllers.ActionFilters.TrackerActionFilter]
		[Authorize(Roles = "customer")]
		public ActionResult Finalized(string key)
		{
			Logger.Info("paypal finalized");

			var subject = new
			{
				Code = string.Empty,
				ExpirationDate = DateTime.MinValue,
				IsNewCustomer = false,
				Password = string.Empty,
				CartCode = string.Empty,
			};
			var result = CryptoService.Decrypt(key, subject);
			var code = Convert.ToString(result[0]);
			var expirationDate = Convert.ToDateTime(result[1]);
			var isNewCustomer = Convert.ToBoolean(result[2]);
			var password = Convert.ToString(result[3]);
			var cartCode = Convert.ToString(result[4]);
			if (expirationDate < DateTime.Now)
			{
				// Tentative probable de hack
				Logger.Warn("Tentative probable de hack sur la commande {0}", code);
				return RedirectToERPStoreRoute(ERPStore.Web.Routing.ERPStoreRoutes.HOME);
			}

			// Envoi du mail de confirmation
			try
			{
				var order = SalesService.GetOrderByCode(code);
				ViewData.Model = order;
				Logger.Debug("Tentative d'envoi de l'email de confirmation de commande");
				if (!isNewCustomer)
				{
					password = string.Empty;
				}
				this.SendCustomerOrderConfirmation(order, password);
				this.SendAdminCustomerOrderConfirmation(order, cartCode, Request.UserHostAddress);
				Logger.Debug("Envoi de la confirmation de commande reussi");
			}
			catch (Exception ex)
			{
				LogError(Logger, ex);
			}

			var viewName = PaypalService.FinalizedViewName;
			if (!viewName.StartsWith("~/"))
			{
				viewName = string.Format("~/views/checkout/{0}", PaypalService.FinalizedViewName);
			}
			return View(viewName);
		}

		private void SendEmail(string subject, string body)
		{
			var settings = GlobalConfiguration.Configuration.Settings;

			var message = new MailMessage();
			message.Body = body;
			message.To.Add(new MailAddress(settings.Contact.ContactEmail));
			message.Subject = "Paypal : " + subject;
			message.IsBodyHtml = false;
			message.From = new MailAddress(GlobalConfiguration.Configuration.Settings.Contact.ContactEmail);

			try
			{
				EmailerService.Send(message);
			}
			catch (Exception ex)
			{
				Logger.Error(ex);
			}
		}
	}
}
